function phi = fvm_soln_SH_waves_2D( x_edges_vec, z_edges_vec, ...
                     x_P_vec, z_P_vec, nt, dt, Gamma_P, rho, S_C, S_P, ...
                      bc_u, bc_d, bc_e, bc_w, phi_0 )
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  find transient phi(x,z), for SH wave equation with shear
%%  modulus Gamma_P.
%%
%%    d^2(rho phi)/dt^2  = div( Gamma grad(phi) ) 
%%
%%  Uses Patankar Finite Volume method.
%%
%%  Boundary Conditions:
%%   All boundaries are finite-volume interfaces
%%   (Patankar Practice B, page 69)
%%   However, there is an additional "point" X_B located on boundary.
%%   Its value and coefficients are determined by phi value or gradient
%%   boundary conditions there.
%%   Adjacent interior point is X_I
%%   (X_I - X_B) = dX_B
%%
%%    phi_B = phi_0
%%        a_B = 1,  all other a_J = 0
%%        b   = phi_0
%%
%%    d phi/dx = dphi_dx_0
%%        use gradient between X_B and X_I (interior point)
%%        d phi/dx|B = Gamma_I (phi_I - phi_B)/dx_B
%%    or
%%        phi_B = dphi_dx_0 * dx_B + phi_I
%%
%%  For each boundary there is a matrix bc_x
%%     with either 2 rows (Up,Down) or 2 columns (East,West).
%%  e.g. East  
%%   bc_e is [nz_P x 2] where nz_P is number of nodes on boundary 
%%     - first column contains value of BC at that node on boundary
%%     - second column specifies type of BC
%%            - 1 -> specified phi
%%            - 2 -> specified phi gradient across boundary
%%
%%   Similarly bc_w, bc_u, bc_d on other boundaries
%%
%%
%%  a_P = coefficient of phi_P at point P = (i,j)
%%  a_E = coefficient of phi_E at point east of (i,j)
%%  a_E, a_W, a_U, a_D for points East, West, Up, Down
%%
%%  System of equations to be solved for phi
%%
%%       big_A * phi = rhs
%%
%%  big_A = matrix with coefficients a_E etc, modified to include BC
%%  phi   = unknown values in Control-volumes
%%  rhs   = right-hand-side column vector containing sources and BC terms
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
    global source_spot  source_period  source_height 

    global dt_plot  fig_num

%
% get number of Finite-Volume nodes
% ----------------------------------
    nx_P = length(x_P_vec);
    nz_P = length(z_P_vec);
%
% get number of finite-volume faces
     nz_fv = nz_P - 2;
     nx_fv = nx_P - 2;
%
% set up arrays defining nodes locations
     X_P = repmat(x_P_vec, nz_P, 1);
     Z_P = repmat(z_P_vec, 1, nx_P);


%  get dX, dZ - spacings between volume edges
%  -------------------------------------------
   %%    dx_vec = [ 0  diff(x_edges_vec)  0];
   %%    dz_vec = [ 0  diff(z_edges_vec)'  0]';
   %% This leads to division by zero for Peclet numbers
       dx_vec = [ 1  diff(x_edges_vec)  1 ];
       dz_vec = [ 1  diff(z_edges_vec)'  1 ]';
%
      dX = repmat( dx_vec, nz_P, 1 );
      dZ = repmat( dz_vec, 1, nx_P );
%

%
%  get dX_E, dx_W, dZ_U, dZ_D - 
%          - distances to adjacent volume centers
%  -----------------------------------------------
%
       dX_e = diff(X_P')';
    % add dummy row at eastern edge to maintain size [nx_P, nz_P]
       dX_e = [ dX_e dX_e(:,end) ];
%
       dX_w = diff(X_P')';
    % add dummy row at western edge to maintain size [nx_P, nz_P]
       dX_w = [ dX_w(:,1) dX_w ];
%
       dZ_u = diff(Z_P);
    % add dummy row at top edge to maintain size [nx_P, nz_P]
       dZ_u = [ dZ_u(1,:)' dZ_u' ]';
%
       dZ_d = diff(Z_P);
    %  add dummy row at bottom edge to maintain size [nx_P, nz_P]
       dZ_d = [ dZ_d' dZ_d(end,:)' ]';

%

%  get f_e, f_w, f_u, f_d 
%          - fractional distances to interface in adjacent volume
%  --------------------------------------------------------------
       f_e = [(1 -(x_edges_vec -x_P_vec(1:end-1))./dX_e(1,1:end-1)) 0];
       f_e = repmat( f_e, nz_P, 1 );
       
       f_w = [0 (1-(x_P_vec(2:end) -x_edges_vec )./dX_w(1,2:end) )];
       f_w = repmat( f_w, nz_P, 1 );
       
       f_u = [0 (1 -(z_P_vec(2:end) -z_edges_vec)./dZ_u(2:end,1) )']';
       f_u = repmat( f_u, 1, nx_P );
%
     f_d = [(1 -(z_edges_vec -z_P_vec(1:end-1) )./dZ_d(1:end-1,1))' 0]';
     f_d = repmat( f_d, 1, nx_P );
%



% set up b matrix - includes constant part of source matrix S 
%         S = S_C + S_P * phi_P    (units are W m^-3)
% ----------------------------------------------------------
    b_0 = S_C.* dX.* dZ;
%
%
% set up matrices for conductivities in adjacent volumes
% ------------------------------------------------------
    Gamma_E   =  [ Gamma_P(:,2:end) Gamma_P(:, end) ];
    Gamma_W   =  [ Gamma_P(:,1) Gamma_P(:,1:end-1) ];
    Gamma_U   =  [ Gamma_P(1,:)' Gamma_P(1:end-1,:)' ]';
    Gamma_D   =  [ Gamma_P(2:end,:)' Gamma_P(end,:)' ]';
%
%
% set up interface conductivity matrices
% --------------------------------------
    Gamma_e =  1./ ( (1 - f_e)./Gamma_P + f_e./Gamma_E );
    Gamma_w =  1./ ( (1 - f_w)./Gamma_P + f_w./Gamma_W );
    Gamma_u =  1./ ( (1 - f_u)./Gamma_P + f_u./Gamma_U );
    Gamma_d =  1./ ( (1 - f_d)./Gamma_P + f_d./Gamma_D );
%
%
%
% set up coefficients at adjacent points
% ---------------------------------------
     a_E = (Gamma_e./dX_e).* dZ;
     a_W = (Gamma_w./dX_w).* dZ;
     a_U = (Gamma_u./dZ_u).* dX;
     a_D = (Gamma_d./dZ_d).* dX;


     a_P_0  = rho.* dX.*dZ./ dt^2;
%
%         
% set up coefficient at central point P (incl source dependent on phi)
% --------------------------------------------------------------------
    a_P = a_E + a_W + a_U + a_D + a_P_0 - S_P.* dX.* dZ; 
%
%
%       
%  plot phi_0 initial condition
%  -----------------------------
        figure(fig_num)
        az = 0;
        el = 90;
        caxis( [-1 1] )
        colorbar

        surf(X_P, Z_P, phi_0 )
        set(gca,'zlim',[-2,2] ) 
        caxis( [-1 1] )
        view(az, el)
        grid on; box on
        xlabel('Distance  (m)')
        ylabel('Depth  (m)')
        zlabel('Temperature  (deg C)')
        title('time = 0')
        pause

%

%%%%%%%%%%%%%%%%%%%%%%%%%
%   time step loop here
%%%%%%%%%%%%%%%%%%%%%%%%%
%  current soln:           phi_t
%  soln at past time step: phi_t_minus_1
%  soln 2 steps back:      phi_t_minus_2
%  Boundary conditions are reset each time step because b 
%  depends on phi_t_0 and phi_t_m1 (solution at previous 2 time steps)
%  While this may be redundant for modifications to the
%  coefficients a_E etc, it may be conceptually clearer to 
%  set the coefficients at the boundary at the same time
%  as we set the values for b, which _does_ depend on time
%  for transient boundary conditions, and when old solution
%  _phi_t_0 changes with time 
%
%
%  phi stores _all_ solutions through time.
%  This will get cumbersome with very many time steps!
%  You may want to replace this with a different way 
%  to store results
% ----------------------------------------------------------
%
%
%  put initial condition phi_0 into big storage matrix phi
    phi = [ phi_0 ];
%
%  put initial condition into phi_t, which carries solution 
%  forward from previous time step
    phi_t_minus_2 = phi_0;
    phi_t_minus_1 = phi_0;
    phi_t         = phi_0;
%
%    get locations for time message on plot
    xtext = min(min(X_P)) + 0.1 * ( max(max(X_P))-min(min(X_P)) ); 
    ztext = min(min(Z_P)) + 0.1 * ( max(max(Z_P))-min(min(Z_P)) ); 
    
%%%%%%%%%%%%%%%%%%%%%%%%%
    for i_time = 1:nt
%%%%%%%%%%%%%%%%%%%%%%%%%
%
    time = i_time * dt;
    message = [ 'time = ' num2str(time) ]
%
  %  set solution at previous 2 time steps
    phi_t_minus_2 = phi_t_minus_1;
    phi_t_minus_1 = phi_t;
%
%   include old solution (initial condition) in rhs vector
        b = b_0 + a_P_0 .* ( 2 * phi_t_minus_1 - phi_t_minus_2 );
%
%
  %  set Boundary Conditions
  %%%%%%%%%%%%%%%%%%%%%%%%%%%
  %   specified values phi_P = phi_value at x_P
  %     set a_P = 1, all other a_X = 0
  %           b = phi_value = bc_x(index, 1);
%
  %  specified gradients
  %     gradient = (phi_I - phi_P)/dX_I  where dX_I = (X_I - X_P)
  %  or
  %     phi_P = phi_I + dX_I * gradient 
  %  so
  %    a_P = 1
  %    a_I = 1
  %    b   = dX_I * gradient
  %  -------------------------------------
%
  %  East Boundary
  %  --------------
       bc_type = bc_e(:,2);
%
       a_P(:,end) = 1; 
       a_U(:,end) = 0;
       a_D(:,end) = 0;
       a_W(:,end) = 0;
       a_E(:,end) = 0;
%
   % index identifies boundary nodes where phi is specified
       index = find(bc_type == 1);
       if( ~isempty(index) )
          b(index, end) = bc_e(index, 1);
       end
%
   % index identifies boundary nodes where gradient is specified
       index = find( bc_type == 2 );
       if( ~isempty(index) )
          a_W(index, end) = 1;
          b(index, end)   = dX_e(index, end).* bc_e(index, 1);
       end
%
%
  %  West Boundary
  %  --------------
       bc_type = bc_w(:,2);

       a_P(:,1) = 1; 
       a_U(:,1) = 0;
       a_D(:,1) = 0;
       a_W(:,1) = 0;
       a_E(:,1) = 0;
%
    % index identifies boundary nodes where phi is specified
       index = find( bc_type == 1 );
       if( ~isempty(index) )
          b(index, 1) = bc_w(index, 1);
       end
%
    % index identifies boundary nodes where gradient is specified
       index = find( bc_type == 2 );
       if( ~isempty(index) )
          a_E(index, 1) = 1;
          b(index, 1)   = - dX_w(index,1).* bc_w(index, 1);
       end
%
%
%
  %  Up Boundary
  %  --------------
       bc_type = bc_u(2,:);

       a_P(1,:) = 1; 
       a_U(1,:) = 0;
       a_D(1,:) = 0;
       a_W(1,:) = 0;
       a_E(1,:) = 0;
%
    % index identifies boundary nodes where phi is specified
       index = find( bc_type == 1 );
       if( ~isempty(index) )
          b(1, index) = bc_u(1, index);
       end
%
    %  now put in source
       if( time <= source_period )
          b(1, source_spot) = source_height ...
                 * sin( 2*pi*i_time*dt/source_period );
       end
%
    % index identifies boundary nodes where gradient is specified
       index = find( bc_type == 2 );
       if( ~isempty(index) )
          a_D(1, index) = 1;
          b(1, index)   = - dZ_d(1, index).* bc_u(1, index);
       end
%
%
  %  Down Boundary
  %  --------------
       bc_type = bc_d(2,:);

       a_P(end,:) = 1; 
       a_U(end,:) = 0;
       a_D(end,:) = 0;
       a_W(end,:) = 0;
       a_E(end,:) = 0;
%
    % index identifies boundary nodes where phi is specified
       index = find( bc_type == 1 );
       if( ~isempty(index) )
          b(end, index) = bc_d(1, index);
       end
%
    % index identifies boundary nodes where gradient is specified
       index = find(bc_type == 2);
       if( ~isempty(index) )
          a_U(end, index) = 1;
          b(end, index)   = dZ_u(end, index).* bc_d(1, index);
       end
%%
%
%

   %   Now call matrix solver
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%
        phi_t = solver( a_E, a_W, a_U, a_D, a_P, b );
%
%
%       
  %  plot phi(x,z) solution
  %  ------------------------
        if( rem(time, dt_plot) <= dt/2 )
          figure(fig_num)
          caxis( [-1 1] )        
          surf(X_P, Z_P, phi_t, 'linestyle', 'none' )
          view(az,el);
          set(gca,'zlim',[-2,2] ) 
          caxis( [-1 1] )
          axis ij
          colorbar
          xlabel('Distance ')
          ylabel('Depth ')
          title( message )         
          pause
 
    %  end plotting loop
        end      % (if( rem(time, dt_plot) ...
 
 %  end time-step loop
 %%%%%%%%%%%%%%%%%%%%%
    end          % for i_time = 1:nt ...
%
%  bring back final state
    phi = phi_t
%
%
